/* 
 *  Incub - incubate your baby robot
 *  Copyright (C) 2008 Stanislaw Szymczyk
 *  e-mail: sszymczy@gmail.com   www: http://cogville.cog-lab.com
 *
 *  This file is part of Incub.
 *
 *  Incub is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  Incub is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Incub.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
 
#include "Xmlable.h"

using std::map;
using std::string;
using std::vector;

namespace xml {
	std::map<const char*, const char*, strCmp> Xmlable::supers;
	std::map<const char*, Xmlable*(*)(void), strCmp> Xmlable::constructors;
	std::map<const char*, void(*)(Xmlable*, Xmlable*), strCmp> Xmlable::adders;
	std::map<const char*, void(*)(Xmlable*, Xmlable*), strCmp> Xmlable::typed_setters;
	std::map<const char*, void(*)(Xmlable*, std::string), strCmp> Xmlable::setters;
		
Xmlable* Xmlable::Create(char *name) {
	if(constructors.find(name) == constructors.end()) throw NoSuchClassException(name);
	Xmlable *entity = constructors[name](); 
	entities.push_back(entity);
	return entity;
};

void Xmlable::AddEntity(Xmlable *entity) {
	const char *entityType, *thisType;
	entityType = entity->type();
	thisType = this->type();
	// first we are searching for subtypes of entity
	while(adders.find((string(thisType)+"_"+string(entityType)).c_str()) == adders.end()) {
		if(supers.find(entityType) == supers.end()) {
			// if searching for entity subtypes failed, go one step up in current object inheritance hierarchy
			// and restart search for entity subtypes
			if(supers.find(thisType) == supers.end()) throw NoSuchAdderException((char*)(string(thisType)+"_"+string(entityType)).c_str());
			thisType = supers[thisType];
			entityType = entity->type();
		} else 	entityType = supers[entityType];
	}
//	std::cout << "adding Entity of type " << entity->type() << " to type " << this->type() << std::endl;
	(*adders[(string(thisType)+"_"+string(entityType)).c_str()])(this,entity);
}

void Xmlable::SetAttribute(string name, string value) {
	const char* entityType = this->type();
	while(setters.find((string(entityType)+"_"+name).c_str()) == setters.end()) {
		if(supers.find(entityType) == supers.end()) throw NoSuchSetterException((char*)strdup(name.c_str()));
		entityType = supers[entityType];
	}
//	cout << "setting attribute in " << this->getType() << endl;
	(*setters[(string(entityType)+"_"+name).c_str()])(this,value);
}

void Xmlable::SetAttribute(Xmlable *entity) {
	const char *entityType, *thisType;
	entityType = entity->type();
	thisType = this->type();
	// first we are searching for subtypes of entity
	while(typed_setters.find((string(thisType)+"_"+string(entityType)).c_str()) == typed_setters.end()) {
		if(supers.find(entityType) == supers.end()) {
			// if searching for entity subtypes failed, go one step up in current object inheritance hierarchy
			// and restart search for entity subtypes
			if(supers.find(thisType) == supers.end()) throw NoSuchSetterException((char*)(string(thisType)+"_"+string(entityType)).c_str());
			thisType = supers[thisType];
			entityType = entity->type();
		} else	entityType = supers[entityType];
	}
//	std::cout << "setting Entity of type " << entity->type() << " in type " << this->type() << std::endl;
	(*typed_setters[(string(thisType)+"_"+string(entityType)).c_str()])(this,entity);
}

void Xmlable::cleanup() {
	for (vector<Xmlable*>::iterator entity = entities.begin(); entity != entities.end(); entity++) {
		delete *entity;
	}	
	for(std::map<const char*, const char*, strCmp>::iterator it = supers.begin(); it != supers.end(); it++) free((void*)it->first);
	for(std::map<const char*, Xmlable*(*)(void), strCmp>::iterator it = constructors.begin(); it != constructors.end(); it++) free((void*)it->first);
	for(std::map<const char*, void(*)(Xmlable*, Xmlable*), strCmp>::iterator it = adders.begin(); it != adders.end(); it++) free((void*)it->first);
	for(std::map<const char*, void(*)(Xmlable*, Xmlable*), strCmp>::iterator it = typed_setters.begin(); it != typed_setters.end(); it++) free((void*)it->first);
	for(std::map<const char*, void(*)(Xmlable*, std::string), strCmp>::iterator it = setters.begin(); it != setters.end(); it++) free((void*)it->first);
}

Xmlable* genesis(ticpp::Element* node) {
//	std::cout << "creating: " << node->Value() << std::endl;
	Xmlable *entity = Xmlable::Create((char*)node->Value().c_str());
	ticpp::Iterator< ticpp::Attribute > attribute;

	for (attribute = attribute.begin(node); attribute != attribute.end(); attribute++) {
		if(attribute->Name() == "xmlns" || attribute->Name() == "xmlns:xsi" || attribute->Name() == "xsi:schemaLocation") continue;
		entity->SetAttribute(attribute->Name(), attribute->Value());
	}
	ticpp::Iterator< ticpp::Element > child;
	for(child = child.begin(node); child != child.end(); child++)	 {
		Xmlable* subEntity = genesis(&(*child));
		try {
			entity->AddEntity(subEntity);
		} catch(Xmlable::NoSuchAdderException& ex1) {
			try {
				entity->SetAttribute(subEntity);
			} catch(Xmlable::NoSuchSetterException& ex2) {
				throw ex1;
			}
		}
	}

	return entity;
}
std::vector<Xmlable*> Xmlable::entities;

}
